# LVGL indev driver for evdev mouse device
# (for the unix micropython port)

import ustruct
import select
import lvgl as lv
import _thread
import time

# Default crosshair cursor
#全屏虚线十字指针
# class crosshair_cursor:
    # def __init__(self, scr=None):
        # self.scr = scr if scr else lv.scr_act()
        # self.hor_res = self.scr.get_width()
        # self.ver_res = self.scr.get_height()
        # self.cursor_style = lv.style_t()
        # self.cursor_style.set_line_width(3)
        # self.cursor_style.set_line_dash_gap(5)
        # self.cursor_style.set_line_dash_width(1)
        # self.cursor_hor = lv.line(self.scr)
        # self.cursor_hor.add_style(self.cursor_style, lv.PART.MAIN)
        # self.cursor_ver = lv.line(self.scr)
        # self.cursor_ver.add_style(self.cursor_style, lv.PART.MAIN)

    # def __call__(self, data):
        # # print("%d : %d:%d" % (data.state, data.point.x, data.point.y))
        # self.cursor_hor.set_points([{'x':0,'y':data.point.y},{'x':self.hor_res,'y':data.point.y}],2)
        # self.cursor_ver.set_points([{'y':0,'x':data.point.x},{'y':self.ver_res,'x':data.point.x}],2)

    # def delete(self):
        # self.cursor_hor.delete()
        # self.cursor_ver.delete()

#小十字指针
class crosshair_cursor:
    def __init__(self, scr=None):
        self.scr = scr if scr else lv.scr_act()
        self.line_width = 20
        self.hor_res = self.scr.get_width()
        self.ver_res = self.scr.get_height()
        self.cursor_style = lv.style_t()
        self.cursor_style.set_line_width(2)
        #self.cursor_style.set_line_dash_gap(5)
        #self.cursor_style.set_line_dash_width(1)
        self.cursor_hor = lv.line(self.scr)
        self.cursor_hor.add_style(self.cursor_style, lv.PART.MAIN)
        self.cursor_ver = lv.line(self.scr)
        self.cursor_ver.add_style(self.cursor_style, lv.PART.MAIN)

    def __call__(self, data):
        # print("%d : %d:%d" % (data.state, data.point.x, data.point.y))
        self.cursor_hor.set_points([{'x':data.point.x-self.line_width,'y':data.point.y},{'x':data.point.x+self.line_width,'y':data.point.y}],2)
        self.cursor_ver.set_points([{'y':data.point.y-self.line_width,'x':data.point.x},{'y':data.point.y+self.line_width,'x':data.point.x}],2)

    def delete(self):
        self.cursor_hor.delete()
        self.cursor_ver.delete()

# evdev driver for mouse
class mouse_indev:
    def __init__(self, scr=None, cursor=None, device='/dev/input/mice'):

        # Open evdev and initialize members
        self.evdev = open(device, 'rb')
        self.poll = select.poll()
        self.poll.register(self.evdev.fileno())
        self.scr = scr if scr else lv.scr_act()
        self.cursor = cursor if cursor else crosshair_cursor(self.scr)
        self.hor_res = self.scr.get_width()
        self.ver_res = self.scr.get_height()

        # Register LVGL indev driver
        self.indev_drv = lv.indev_drv_t()
        self.indev_drv.init()
        self.indev_drv.type = lv.INDEV_TYPE.POINTER
        self.indev_drv.read_cb = self.mouse_read
        self.indev = self.indev_drv.register()

    def mouse_read(self, indev_drv, data) -> int:
        
        # Check if there is input to be read from evdev
        if not self.poll.poll()[0][1] & select.POLLIN:
            return 0

        # Read and parse evdev mouse data
        mouse_data = ustruct.unpack('bbb',self.evdev.read(3))

        # Data is relative, update coordinates
        data.point.x += mouse_data[1]
        data.point.y -= mouse_data[2]

        # Handle coordinate overflow cases
        data.point.x = min(data.point.x, self.hor_res - 1)
        data.point.y = min(data.point.y, self.ver_res - 1)
        data.point.x = max(data.point.x, 0)
        data.point.y = max(data.point.y, 0)

        # Update "pressed" status
        data.state = lv.INDEV_STATE.PRESSED if ((mouse_data[0] & 1) == 1) else lv.INDEV_STATE.RELEASED

        # Draw cursor, if needed
        if self.cursor: self.cursor(data)
        return 0

    def delete(self):
        self.evdev.close()
        if self.cursor and hasattr(self.cursor, 'delete'):
            self.cursor.delete()
        self.indev.enable(False)

# evdev driver for touchscreen
# 由于lvgl不具备线程安全，使用tp_read_callback回调函数时必须小心，否则可能会导致崩溃
class tp_indev:
    def __init__(self, scr=None, cursor=None, device='/dev/input/event0',tp_size=(None,None), tp_read_callback=None):

        # Open evdev and initialize members
        self.evdev = open(device, 'rb')
        self.poll = select.poll()
        self.poll.register(self.evdev.fileno())
        self.scr = scr if scr else lv.scr_act()
        self.cursor = cursor if cursor else crosshair_cursor(self.scr)
        self.hor_res = self.scr.get_width()
        self.ver_res = self.scr.get_height()
        self.tmp_x = 0
        self.tmp_y = 0
        self.tmp_state = 0
        self.tp_move_flag = False
        if tp_size[0] == None:
            self.x_k = 1
        else:
            self.x_k = self.hor_res/tp_size[0] #width
        if tp_size[1] == None:
            self.y_k = 1
        else:
            self.y_k = self.ver_res/tp_size[1] #height

        # Register LVGL indev driver
        self.indev_drv = lv.indev_drv_t()
        self.indev_drv.init()
        self.indev_drv.type = lv.INDEV_TYPE.POINTER
        self.indev_drv.read_cb = self.touch_read
        self.indev = self.indev_drv.register()

        #touch read thread
        _thread.start_new_thread( self.tp_read_thread, ("tp_read_thread", ) )
        self.tp_read_callback = tp_read_callback

    #@micropython.native
    def tp_read_thread(self, name):
        print("thread name:",name)
        while True:
            while (self.poll.poll()[0][1] & select.POLLIN):
                self.tp_move_flag = True
                # Read and parse evdev touch data
                # for 32bit cpu
                #touch_data = ustruct.unpack('<IIHHi', self.evdev.read(16))
                # for 64bit cpu
                touch_data = ustruct.unpack('<QQHHi', self.evdev.read(24))
                #touch_timeS = touch_data[0]
                #touch_timeuS = touch_data[1]
                touch_type = (int)(touch_data[2])
                touch_code = (int)(touch_data[3])
                touch_value = (int)(touch_data[4])
                #print("touch_data: type:%02X, code:%02X,value:%d" % (touch_type ,touch_code, touch_value))
                # Data is relative, update coordinates
                if touch_type == 0x03:
                    if touch_code == 0x35:
                        self.tmp_x = touch_value
                        #print("touch X:",touch_value)
                    elif touch_code == 0x36:
                        self.tmp_y = touch_value
                        #print("touch Y:",touch_value)
                elif touch_type == 0x01:
                    if touch_code == 0x14a:
                        self.tmp_state = lv.INDEV_STATE.PRESSED if (touch_value == 1) else lv.INDEV_STATE.RELEASED
                elif touch_type == 0x00:
                    # if self.tp_read_callback != None: #self.tmp_state == lv.INDEV_STATE.PR and
                        # self.tp_read_callback(self.tmp_state, (int)(self.tmp_x * self.x_k), (int)(self.tmp_y * self.y_k))
                    pass
            time.sleep_ms(20)
            self.tp_move_flag = False
    #@micropython.native
    def touch_read(self, indev_drv, data) -> int:
        data.point.x = (int)(self.tmp_x * self.x_k)
        data.point.y = (int)(self.tmp_y * self.y_k)
        data.state = self.tmp_state

        #print("X:%d, Y:%d" % (data.point.x, data.point.y))
        if self.tp_move_flag == True:
            # Draw cursor, if needed
            if self.cursor : self.cursor(data)
            if self.tp_read_callback != None:
                self.tp_read_callback(self.tmp_state, (int)(self.tmp_x * self.x_k), (int)(self.tmp_y * self.y_k))
        return 0

    def delete(self):
        self.evdev.close()
        if self.cursor and hasattr(self.cursor, 'delete'):
            self.cursor.delete()
        self.indev.enable(False)
